package com.ipan.kits.xconverter;

import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.Validate;

import com.ipan.kits.xconverter.XConverters.BigDecimalConverter;
import com.ipan.kits.xconverter.XConverters.BigIntegerConverter;
import com.ipan.kits.xconverter.XConverters.BooleanConverter;
import com.ipan.kits.xconverter.XConverters.ByteArrayConverter;
import com.ipan.kits.xconverter.XConverters.ByteConverter;
import com.ipan.kits.xconverter.XConverters.DateConverter;
import com.ipan.kits.xconverter.XConverters.DoubleArrayConverter;
import com.ipan.kits.xconverter.XConverters.DoubleConverter;
import com.ipan.kits.xconverter.XConverters.FloatArrayConverter;
import com.ipan.kits.xconverter.XConverters.FloatConverter;
import com.ipan.kits.xconverter.XConverters.IntegerArrayConverter;
import com.ipan.kits.xconverter.XConverters.IntegerConverter;
import com.ipan.kits.xconverter.XConverters.LongArrayConverter;
import com.ipan.kits.xconverter.XConverters.LongConverter;
import com.ipan.kits.xconverter.XConverters.PrimitiveDoubleArrayConverter;
import com.ipan.kits.xconverter.XConverters.PrimitiveFloatArrayConverter;
import com.ipan.kits.xconverter.XConverters.PrimitiveIntegerArrayConverter;
import com.ipan.kits.xconverter.XConverters.PrimitiveLongArrayConverter;
import com.ipan.kits.xconverter.XConverters.ShortConverter;
import com.ipan.kits.xconverter.XConverters.SqlDateConverter;
import com.ipan.kits.xconverter.XConverters.TimeConverter;
import com.ipan.kits.xconverter.XConverters.TimestampConverter;

/**
 * 类型转换器
 * 支持String与具体类型互转，以及具体类型与具体类型的转换；
 * 
 * 普通类型转换器使用XTypeConverter.me()；
 * 带有Date模糊匹配的注册器使用XTypeConverter.FuzzyConverter，应用的场合是日期时间的入参可能出现多种情况的场景；
 * 支持XConverters.defaultParsePattern定义的格式，如下：
 * 		"yyyy-MM-dd HH:mm:ss", 
 * 		"yyyy/MM/dd HH:mm:ss", 
 * 		"yyyyMMdd HH:mm:ss",
 * 		"yyyy-MM-dd",
 * 		"yyyy/MM/dd",
 * 		"yyyyMMddHHmmss",
 * 		"yyyyMMdd"
 * 若仍然不够用，则使用自定义类型转换XTypeConverter.create()自己去给要转换的类型做转换实现；
 * 
 * @author iPan
 * @version 2019-06-07
 */
public class XTypeConverter {

	private final Map<Class<?>, IXConverter<?>> converterMap = new HashMap<Class<?>, IXConverter<?>>();
	private static XTypeConverter me = new XTypeConverter();
	public static final XTypeConverter FuzzyConverter = createFuzzyParseDateConverter(); // 模糊匹配转换器，作为系统单例使用，禁止在外部修改注册配置！
	private boolean regOn = true; // 允许注册
	
	public static interface Init {
		void init(XTypeConverter converter);
	}
	
	private XTypeConverter() {
		regist(Integer.class, new IntegerConverter());
		regist(int.class, new IntegerConverter());
		regist(Long.class, new LongConverter());
		regist(long.class, new LongConverter());
		regist(Double.class, new DoubleConverter());
		regist(double.class, new DoubleConverter());
		regist(Float.class, new FloatConverter());
		regist(float.class, new FloatConverter());
		regist(Boolean.class, new BooleanConverter());
		regist(boolean.class, new BooleanConverter());
		regist(java.util.Date.class, new DateConverter()); // 默认支持格式：yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm、yyyy-MM-dd
		regist(java.sql.Date.class, new SqlDateConverter()); // 默认支持格式：yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm、yyyy-MM-dd
		regist(java.sql.Time.class, new TimeConverter()); // 默认支持格式：HH:mm:ss、HH:mm
		regist(java.sql.Timestamp.class, new TimestampConverter()); // 默认支持格式：yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm、yyyy-MM-dd
		regist(java.math.BigDecimal.class, new BigDecimalConverter());
		regist(java.math.BigInteger.class, new BigIntegerConverter());
		regist(byte[].class, new ByteArrayConverter());
		regist(Short.class, new ShortConverter());
		regist(short.class, new ShortConverter());
		regist(Byte.class, new ByteConverter());
		regist(byte.class, new ByteConverter());
		// 扩展类型转换
		regist(Integer[].class, new IntegerArrayConverter());
		regist(int[].class, new PrimitiveIntegerArrayConverter());
		regist(Long[].class, new LongArrayConverter());
		regist(long[].class, new PrimitiveLongArrayConverter());
		regist(Double[].class, new DoubleArrayConverter());
		regist(double[].class, new PrimitiveDoubleArrayConverter());
		regist(Float[].class, new FloatArrayConverter());
		regist(float[].class, new PrimitiveFloatArrayConverter());
	}
	
	public static XTypeConverter me() {
		return me;
	}
	
	public static XTypeConverter create(Init init) {
		XTypeConverter defaultCvt = new XTypeConverter();
		init.init(defaultCvt);
		return defaultCvt;
	}
	private static XTypeConverter createFuzzyParseDateConverter() {
		XTypeConverter cvt = new XTypeConverter();
		cvt.regist(java.util.Date.class, DateConverter.createDefaultDateConverter());
		cvt.regist(java.sql.Date.class, SqlDateConverter.createDefaultSqlDateConverter());
		cvt.regist(java.sql.Time.class, TimeConverter.createDefaultTimeConverter());
		cvt.regist(java.sql.Timestamp.class, TimestampConverter.createDefaultTimestampConverter());
		cvt.regOn = false;
		return cvt;
	}
	
	public <T> void regist(Class<T> type, IXConverter<T> converter) {
		if (!regOn) {
			throw new RuntimeException("该注册器禁止使用类型注册");
		}
		converterMap.put(type, converter);
	}
	
	/**
	 * 字符串=>具体类型（弱类型=>强类型）
	 * @param type 需要转换成为的数据类型
	 * @param s 被转换的 String 类型数据，注意： s 参数不接受 null 值，否则会抛出异常
	 * @return 转换成功的数据
	 */
	public final <T> T convert(Class<T> type, String s) throws ParseException {
		Validate.notNull(type);
		if (s == null) {
			return null;
		}
		// mysql type: varchar, char, enum, set, text, tinytext, mediumtext, longtext
		if (type == String.class) {
			return (T) ("".equals(s) ? null : s);	// 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null.
		}
		s = s.trim();
		if ("".equals(s)) {	// 前面的 String跳过以后,所有的空字符串全都转成 null,  这是合理的
			return null;
		}
		// 以上两种情况无需转换,直接返回, 注意, 本方法不接受null为 s 参数(经测试永远不可能传来null, 因为无输入传来的也是"")
		//String.class提前处理
		
		// --------
		IXConverter<?> converter = converterMap.get(type);
		if (converter != null) {
			return (T) converter.convert(s);
		}
		throw new RuntimeException(type.getName() + "类型转换失败，请注册该类型的转换器！");
	}
	
	/**
	 * 具体类型=>字符串（强类型=>弱类型）
	 */
	public final String unConvert(Object obj) {
		if (obj == null) {
			return null;
		}
		IXConverter<?> converter = converterMap.get(obj.getClass());
		if (converter != null) {
			return converter.unConvert(obj);
		} else {
			return obj.toString();
		}
	}
	
	/**
	 * 具体类型=>具体类型（强类型=>强类型）
	 * 前提：这两种类型必须可以相互转化的，否则抛异常！ 
	 * 比如：Date不可以转Integer，Integer可以转BigDecimal；
	 */
	public final <T> T specConvert(Class<T> type, Object val) throws ParseException {
		if (val == null) {
			return null;
		}
		Validate.notNull(type);
		if (val instanceof String) {
			return convert(type, (String)val);
		}
		String valStr = unConvert(val);
		return convert(type, valStr);
	}

//	public static void main(String[] args) throws ParseException {
//		String s = "20190608";
//		Date d = FuzzyConverter.convert(java.util.Date.class, s);
//		System.out.println(FuzzyConverter.unConvert(d));
//		System.out.println(FuzzyConverter.convert(String.class, "123"));
//	}
}
